/*
 * C3.2.1.c
 *
 *  Created on: 2013/01/23
 *      Author: xun
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rule_info.h"
#include "core.h"
#include "trace.h"
#include "TraceAst.h"

static const PSZ RuleID = "C3.2.1";

//TODO
static const PSZ MESSAGE_ENG = "";
static const PSZ MESSAGE_JPN =
		"仮引数は、指し示しているオブジェクトを変更する目的でポインタが用いられていない場合、constへのポインタとして宣言すべきである。";
static const PSZ MESSAGE_CHS = "";

static const U2 RANK = MSG_RANK_ERR;

static PRULEOUT pRuleHead;
static PRULEOUT pRuleTail;

typedef struct {
	AST astSymbol;
	PSZ pszName;
	INT NumOfUsed;
} TYPE_PARAMSYMNAME;

#define MAX_NUM_PARAM 256

static INT RuleCheck(U2 kind, AST index, U4 event, AST* pCodeScp);
static INT StatCheck(AST index, TYPE_PARAMSYMNAME* StructSymNames,
		INT NumOfParam);
static INT ExprCheck(AST index, TYPE_PARAMSYMNAME* StructSymNames,
		INT NumOfParam);
static INT TEST();

/******************************************************************
 * main logic
 *******************************************************************/
static S4 RuleMain(U2 kind, AST index, U4 event) {
	INT result = RULE_RESULT_OK;
	PRULEOUT newNode = NULL;
	PRULEOUT tmpNode = NULL;
	PRULEOUT tmpNode1 = NULL;

	AST codeScp = 0;

	//TODO
	result = RuleCheck(kind, index, event, &codeScp);

	if (result == RULE_RESULT_OK) {
		return EXIT_SUCCESS;
	}

	/*re-checked?*/
	tmpNode = pRuleHead;
	while (tmpNode != NULL ) {
		if (tmpNode->indexInfo == index) {
			if (result > RULE_RESULT_OK) {
				result = RULE_RESULT_OK;
			} else {
				if (tmpNode == pRuleHead) {
					pRuleHead = pRuleHead->next;
					free(tmpNode);
				} else if (tmpNode == pRuleTail) {
					tmpNode1->next = NULL;
					pRuleTail = tmpNode1;
					free(tmpNode);
				} else {
					tmpNode1->next = tmpNode->next;
					free(tmpNode);
				}
			}
			break;
		}
		tmpNode1 = tmpNode;
		tmpNode = tmpNode->next;
	}

	/*make result struct*/
	if (result > RULE_RESULT_OK) {
		newNode = (PRULEOUT) malloc(sizeof(RULEOUT));
		if (pRuleHead == NULL ) {
			pRuleHead = newNode;
		}

		//TODO
		//codeScp = ??;
		newNode->indexInfo = index;
		newNode->fileIndex = CCodeScope.getFileIndex(codeScp);
		newNode->beginLine = CCodeScope.getBeginLine(codeScp);
		newNode->beginCol = CCodeScope.getBeginLineOffset(codeScp);
		newNode->endLine = CCodeScope.getEndLine(codeScp);
		newNode->endCol = CCodeScope.getEndLineOffset(codeScp);
		newNode->ruleID = RuleID;
		newNode->errRank = RANK;

		//TODO
		sprintf(newNode->msgBuf_eng, MESSAGE_ENG);
		sprintf(newNode->msgBuf_chs, MESSAGE_CHS);
		sprintf(newNode->msgBuf_jpn, MESSAGE_JPN);

		newNode->rfileIndex = 0;
		newNode->rlocLine = 0;
		newNode->next = NULL;

		if (pRuleTail != NULL ) {
			pRuleTail->next = newNode;
		}

		pRuleTail = newNode;
	}

	return EXIT_SUCCESS;
}

VOID C3_2_1_register(VOID) {
	PRULEMAN * pCurRule = &stRuleManager;
	PRULEMAN newRuleMan = (PRULEMAN) malloc(sizeof(RULEMAN));
	memset(newRuleMan, 0, sizeof(RULEMAN));
	newRuleMan->ruleID = RuleID;
	newRuleMan->ruleSwitch = OFF;
	newRuleMan->headOut = &pRuleHead;
	newRuleMan->tailOut = &pRuleTail;
	newRuleMan->ruleMain_ast = RuleMain;
	if (stRuleManager == NULL ) {
		stRuleManager = newRuleMan;
	} else {
		while ((*pCurRule)->nextRule != NULL ) {
			pCurRule = &((*pCurRule)->nextRule);
		}
		(*pCurRule)->nextRule = newRuleMan;
	}
}

static INT RuleCheck(U2 kind, AST index, U4 event, AST* pCodeScp) {
	INT result = RULE_RESULT_OK;

	AST astFuncDeclarator = 0;
	AST astChildDeclarator = 0;
	AST astParamDecl = 0;
	AST astParamDeclarator = 0;
	AST astTypeSpec = 0;
	AST astTypeInfo = 0;
	AST astSymbol = 0;
	AST astFuncBody = 0;
	TYPE_PARAMSYMNAME StructSymNames[MAX_NUM_PARAM];
	INT NumOfParam = 0;

	if (kind != TR_KIND_DECLARATION) {
		return EXIT_SUCCESS;
	}

	if (!(event & EVT_DECLARATION)) {
		return EXIT_SUCCESS;
	}

	astFuncDeclarator = CDeclaration.getDeclarator(index);
	while (astFuncDeclarator != 0
			&& !CDeclarator.isFunctionDeclarator(astFuncDeclarator)) {
		astFuncDeclarator = CDeclarator.getChildDeclarator(astFuncDeclarator);
	}

	// get pointer parameter
	if (astFuncDeclarator != 0
			&& CDeclarator.isFunctionDeclarator(astFuncDeclarator)) {
		astParamDecl = CDeclarator.function_getParamChain(astFuncDeclarator);
		while (astParamDecl != 0 && CDeclaration.isParameter(astParamDecl)) {

			//TypeSpecifier is pointer
			astTypeSpec = CDeclaration.getTypeSpecifier(astParamDecl);
			astTypeInfo = CTypeSpecifier.getTypeInfo(astTypeSpec);
			if (CTypeInfo.isConst(astTypeInfo)) {
				// do nothing , get nextParamDeclaration
			} else if (CTypeInfo.isPointer(astTypeInfo)
					|| CTypeInfo.isArray(astTypeInfo)) {
				astParamDeclarator = CDeclaration.getDeclarator(astParamDecl);
				while (astParamDeclarator != 0
						&& !CDeclarator.isSymbolDeclarator(astParamDeclarator)) {
					astParamDeclarator = CDeclarator.getChildDeclarator(
							astParamDeclarator);
				}
				astSymbol = CDeclarator.symbol_getSymbol(astParamDeclarator);
				StructSymNames[NumOfParam].astSymbol = astSymbol;
				StructSymNames[NumOfParam].pszName = CSymbol.getName(astSymbol);
				StructSymNames[NumOfParam].NumOfUsed = 0;
				NumOfParam++;
			} else {

				//Declarator is pointer
				astParamDeclarator = CDeclaration.getDeclarator(astParamDecl);
				while (astParamDeclarator != 0
						&& !CDeclarator.isPointerDeclarator(astParamDeclarator)
						&& !CDeclarator.isArrayDeclarator(astParamDeclarator)) {
					astParamDeclarator = CDeclarator.getChildDeclarator(
							astParamDeclarator);
				}
				if (astParamDeclarator != 0) {
					while (astParamDeclarator != 0
							&& !CDeclarator.isSymbolDeclarator(
									astParamDeclarator)) {
						astParamDeclarator = CDeclarator.getChildDeclarator(
								astParamDeclarator);
					}
					if (CDeclarator.isSymbolDeclarator(astParamDeclarator)) {
						astSymbol = CDeclarator.symbol_getSymbol(
								astParamDeclarator);
						StructSymNames[NumOfParam].astSymbol = astSymbol;
						StructSymNames[NumOfParam].pszName = CSymbol.getName(
								astSymbol);
						StructSymNames[NumOfParam].NumOfUsed = 0;
						NumOfParam++;
					}
				}

			}

			astParamDecl = CDeclaration.getNextDeclaration(astParamDecl);
		}
	}

	// get function body
	if (astFuncDeclarator != 0
			&& CDeclarator.isFunctionDeclarator(astFuncDeclarator)) {
		astChildDeclarator = CDeclarator.getChildDeclarator(astFuncDeclarator);
		while (astChildDeclarator != 0
				&& !CDeclarator.isSymbolDeclarator(astChildDeclarator)) {
			astChildDeclarator = CDeclarator.getChildDeclarator(
					astChildDeclarator);
		}
		if (astChildDeclarator != 0
				&& CDeclarator.isSymbolDeclarator(astChildDeclarator)) {
			astSymbol = CDeclarator.symbol_getSymbol(astChildDeclarator);
			if (CSymbol.isFunction(astSymbol)) {
				astFuncBody = CSymbol.getFunctionBody(astSymbol);
				//TODO
				if (0) {
					result = TEST();
				}
				result = StatCheck(astFuncBody, StructSymNames, NumOfParam);

			}
		}
	}

	if (result == RULE_RESULT_NG) {
		*pCodeScp = CDeclaration.getCodeScope(index);
	}
	return result;
}

static INT TEST() {
	return 1;
}

static INT StatCheck(AST index, TYPE_PARAMSYMNAME* StructSymNames,
		INT NumOfParam) {
	INT result = RULE_RESULT_NG;

	AST astStat = 0;
	AST astDecl = 0;
	AST astDeclarator = 0;
	AST astExpr = 0;

	if (index == 0) {
		return result;
	}

	if (CStatement.isBlock(index)) {
		astStat = CStatement.block_getChain(index);
		while (astStat != 0) {
			if (StatCheck(astStat, StructSymNames, NumOfParam) == RULE_RESULT_OK) {
				return RULE_RESULT_OK;
			}
			astStat = CStatement.getNextStmt(astStat);
		}
	} else if (CStatement.isBreak(index)) {
		// do nothing
	} else if (CStatement.isCase(index)) {
		astStat = CStatement.case_getStatement(index);
		if (StatCheck(astStat, StructSymNames, NumOfParam) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		}
	} else if (CStatement.isContinue(index)) {
		// do nothing
	} else if (CStatement.isDecl(index)) {
		astDecl = CStatement.dec_getDeclaration(astStat);
		astDeclarator = CDeclaration.getDeclarator(astDecl);
		if (CDeclarator.isInitialExpDeclarator(astDeclarator)) {
			astExpr = CDeclarator.initExp_getInitExp(astDeclarator);
			if (ExprCheck(astExpr, StructSymNames, NumOfParam) == RULE_RESULT_OK) {
				return RULE_RESULT_OK;
			}
		}
	} else if (CStatement.isDefault(index)) {
		astStat = CStatement.default_getStatement(index);
		if (StatCheck(astStat, StructSymNames, NumOfParam) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		}
	} else if (CStatement.isDo(index)) {
		astExpr = CStatement.do_getCondExp(index);
		if (ExprCheck(astExpr, StructSymNames, NumOfParam) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		}
		astStat = CStatement.do_getBody(index);
		if (StatCheck(astStat, StructSymNames, NumOfParam) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		}
	} else if (CStatement.isExpression(index)) {
		astExpr = CStatement.expStatement_getExp(index);
		if (ExprCheck(astExpr, StructSymNames, NumOfParam) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		}
	} else if (CStatement.isFor(index)) {
		astDecl = CStatement.for_getInit_Decl(index);
		astDeclarator = CDeclaration.getDeclarator(astDecl);
		if (CDeclarator.isInitialExpDeclarator(astDeclarator)) {
			astExpr = CDeclarator.initExp_getInitExp(astDeclarator);
			if (ExprCheck(astExpr, StructSymNames, NumOfParam) == RULE_RESULT_OK) {
				return RULE_RESULT_OK;
			}
		}
		astExpr = CStatement.for_getInitExp(index);
		if (ExprCheck(astExpr, StructSymNames, NumOfParam) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		}
		astExpr = CStatement.for_getCondExp(index);
		if (ExprCheck(astExpr, StructSymNames, NumOfParam) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		}
		astExpr = CStatement.for_getIncExp(index);
		if (ExprCheck(astExpr, StructSymNames, NumOfParam) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		}
		astStat = CStatement.for_getBody(index);
		if (StatCheck(astStat, StructSymNames, NumOfParam) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		}

	} else if (CStatement.isGoto(index)) {
		// TODO
	} else if (CStatement.isIf(index)) {
		astExpr = CStatement.if_getCondExp(index);
		if (ExprCheck(astExpr, StructSymNames, NumOfParam) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		}
		astStat = CStatement.if_getBody(index);
		if (StatCheck(astStat, StructSymNames, NumOfParam) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		}
		astStat = CStatement.if_getElseBody(index);
		if (StatCheck(astStat, StructSymNames, NumOfParam) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		}
	} else if (CStatement.isLabel(index)) {
		astStat = CStatement.label_getStatement(index);
		if (StatCheck(astStat, StructSymNames, NumOfParam) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		}
	} else if (CStatement.isReturn(index)) {
		astExpr = CStatement.return_getExp(index);
		if (ExprCheck(astExpr, StructSymNames, NumOfParam) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		}
	} else if (CStatement.isSwitch(index)) {
		astExpr = CStatement.switch_getCondExp(index);
		if (ExprCheck(astExpr, StructSymNames, NumOfParam) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		}
		astStat = CStatement.switch_getBody(index);
		if (StatCheck(astStat, StructSymNames, NumOfParam) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		}
	} else if (CStatement.isWhile(index)) {
		astExpr = CStatement.while_getCondExp(index);
		if (ExprCheck(astExpr, StructSymNames, NumOfParam) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		}
		astStat = CStatement.while_getBody(index);
		if (StatCheck(astStat, StructSymNames, NumOfParam) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		}
	}

	return result;
}

static INT ExprCheck(AST index, TYPE_PARAMSYMNAME* StructSymNames,
		INT NumOfParam) {
	INT result = RULE_RESULT_NG;

	AST astLeftExpr = 0;
	AST astRightExpr = 0;
	U4 u4OpKind = 0;
	AST astExpr = 0;
	AST astParntExpr = 0;
	AST astSymbol = 0;
	PSZ pszName = 0;
	INT i = 0;

	if (index == 0) {
		return result;
	}

	u4OpKind = CExpression.getOpKind(index);
	if (CExpressionOpKind.isIdentifier(u4OpKind)) {

		// is parameter?
		astSymbol = CExpression.getPrimarySymbol(index);
		if (astSymbol != 0) {
			pszName = CSymbol.getName(astSymbol);

			for (i = 0; i < NumOfParam; i++) {
				if (strcmp(pszName, StructSymNames[i].pszName) == 0) {

					// has been changed
					astExpr = index;
					if (CExpression.isParentExp(astExpr)) {
						astParntExpr = CExpression.parent_getExp(astExpr);
					}
					u4OpKind = CExpression.getOpKind(astParntExpr);
					while (CExpressionOpKind.isParentheseExp(u4OpKind)) {
						astExpr = astParntExpr;
						if (CExpression.isParentExp(astExpr)) {
							astParntExpr = CExpression.parent_getExp(astExpr);
							u4OpKind = CExpression.getOpKind(astParntExpr);
						}
					}

					if (CExpressionOpKind.isPostfixOp(u4OpKind)) {
						if (CPostfixOpKind.isFunctionCall(u4OpKind)
								|| CPostfixOpKind.isPostDecrement(u4OpKind)
								|| CPostfixOpKind.isPostIncrement(u4OpKind)) {
							StructSymNames[i].NumOfUsed++;
						}
					} else if (CExpressionOpKind.isUnaryOp(u4OpKind)) {
						if (CUnaryOpKind.isPreDecrement(u4OpKind)
								|| CUnaryOpKind.isPreIncrement(u4OpKind)) {
							StructSymNames[i].NumOfUsed++;
						}
					} else if (CExpressionOpKind.isAssignmentOp(u4OpKind)) {
						if (CExpression.getLeftNode(astParntExpr) == astExpr) {
							StructSymNames[i].NumOfUsed++;
						}
					}

					break;
				}
			}

			for (i = 0; i < NumOfParam; i++) {
				if (StructSymNames[i].NumOfUsed == 0) {
					return RULE_RESULT_NG;
				}
			}
			return RULE_RESULT_OK;
		}
	} else {
		astLeftExpr = CExpression.getLeftNode(index);
		if (ExprCheck(astLeftExpr, StructSymNames, NumOfParam) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		}
		astRightExpr = CExpression.getRightNode(index);
		if (ExprCheck(astRightExpr, StructSymNames,
				NumOfParam) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		}
	}

	return result;
}
